//
//  ADManager.swift
//  ClockWidget
//
//  Created by TSYH on 2023/9/27.
//

import GoogleMobileAds
import AppTrackingTransparency

// 测试设备
private let TestDids  = ["f1874ea201bcf9bccc787826c719b331", // iPhone 11
                        "9544cae26934729be31dfb36ec3fa99d", // 老板娘 iPhone 8
                        "bd45ae62fa8c1cd3dbc24419178174fd", // Gouging's 12
                        "68c8354bd1af44c4974b94a68e6ee04c", // Gouging's 12 ?
                        "24ce3a798632a10f9bb2b5a12e9cad6b", // phpenix
                        "06e0b4ffa24375cc191f0c9866ce77e7", // guangshi
                        "909fb60b4247e9b2beb264a9737d71de", // Zhengrui’s iPhone 8
                        "54566b03b2054986b36125129b584572", // Zhengrui’s iPhone 11
]

public protocol ADManagerLaunchDelegate: NSObjectProtocol where Self: UIViewController {
    func adWillPresent(_ manager: ADManager)
    func adDidDismiss(_ manager: ADManager)
    func adDidFailToPresent(_ manager: ADManager)
}

public extension ADManagerLaunchDelegate {
    func adWillPresent(_ manager: ADManager) {}
    func adDidDismiss(_ manager: ADManager) {}
    func adDidFailToPresent(_ manager: ADManager) {}
}

public extension Notification.Name {
    static let adWillPresentNotification = Notification.Name.init("adWillPresentNotification")
    static let adDidDismissNotification = Notification.Name.init("adDidDismissNotification")
}

public class ADManager: NSObject {
    public static let shared = ADManager()
    /// 是否禁显区域
    public lazy var isForbittenRegion: Bool = false
    /// 是否Vip用户
    public lazy var isVipUser: Bool = false
    
    /// 阻断，优先处理用户事件
    public lazy var isBlockupByEvent: Bool = false
    
    // Private
    weak var delegate: ADManagerLaunchDelegate?
    lazy var adCaches: [ADCacheModel] = []
    lazy var isRequestedTracking: Bool = false
    lazy var appDidFinishLaunch: Bool = false
    
    // 是否显示全屏广告中，（包括开屏、插屏）
    public var isShowingFullScreenAd: Bool {
        return adCaches.contains(where: { $0.isShowing && $0.scene.adType.isFullScreenAD })
    }
    
    override init() {
        super.init()
        
        MobileAds.shared.requestConfiguration.testDeviceIdentifiers = TestDids
        
        isRequestedTracking = UserDefaults.standard.bool(forKey: "K_ATTracking_Did_Request")
    }
    
    var canShowAd: Bool {
//        #if DEBUG
//        return false
//        #endif
        guard !isForbittenRegion else {
            return false
        }
        return !isVipUser
    }
    
    // 跟踪弹窗显示标记
    public func didRequestedTracking() {
        isRequestedTracking = true
        UserDefaults.standard.set(true, forKey: "K_ATTracking_Did_Request")
    }
    
    public func prepareAd(scenes: [ADSceneProtocol]) {
        for scene in scenes {
            requestAd(for: scene)
        }
    }
}

// MARK: 缓存相关
extension ADManager {
    func hasValidAd(for scene: ADSceneProtocol) -> Bool {
        return validAdCache(for: scene)?.ad != nil
    }
    
    // 保存缓存
    private func saveAdCache(with ad: NSObject?, for scene: ADSceneProtocol) {
        if let model = adCaches.first(where: { $0.scene.adId == scene.adId }) {
            model.ad = ad
        } else {
            // 创建缓存
            let cacheModel = ADCacheModel(scene: scene)
            cacheModel.ad = ad
            adCaches.append(cacheModel)
        }
    }
    
    /// 清空指定场景下的广告
    private func clearAd(for scene: ADSceneProtocol) {
        adCaches.first(where: { $0.scene.adId == scene.adId })?.clear()
    }
    
    // 指定场景下的有效广告
    private func validAdCache(for scene: ADSceneProtocol) -> ADCacheModel? {
        guard let adCache = adCaches.first(where: { $0.scene.adId == scene.adId }),
              let _ = adCache.ad, !adCache.isExpired else {
            return nil
        }
        return adCache
    }
}

// MARK: SDK 回调处理
extension ADManager: FullScreenContentDelegate {
    public func adWillPresentFullScreenContent(_ ad: FullScreenPresentingAd) {
        if let current = ad as? NSObject,
            let cacheModel = adCaches.first(where: { $0.ad == current }) {
            cacheModel.isShowing = true
            cacheModel.actionHandler?(.willPresent)
        }
        delegate?.adWillPresent(self)
        NotificationCenter.default.post(name: .adWillPresentNotification, object: nil)
    }

    public func adDidDismissFullScreenContent(_ ad: FullScreenPresentingAd) {
        if let current = ad as? NSObject,
            let cacheModel = adCaches.first(where: { $0.ad == current }) {
            
            let actionHandler = cacheModel.actionHandler
            actionHandler?(.finished)
            
            cacheModel.actionHandler = nil
            
            // 关闭，删除当前已显示的广告
            cacheModel.clear()
            
            // 预加载下一次广告
            requestAd(for: cacheModel.scene)
        }
        
        delegate?.adDidDismiss(self)
        NotificationCenter.default.post(name: .adDidDismissNotification, object: nil)
    }

    public func ad(_ ad: FullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {
        delegate?.adDidFailToPresent(self)
        
        if let current = ad as? NSObject,
            let cacheModel = adCaches.first(where: { $0.ad == current }) {
            
            let actionHandler = cacheModel.actionHandler
            actionHandler?(.fail)
            
            cacheModel.actionHandler = nil
        }
        NotificationCenter.default.post(name: .adDidDismissNotification, object: nil)
    }
}

// MARK: 开屏广告
extension ADManager {
    // 显示开屏广告：冷启动
    public func showLaunchAd(scene: ADSceneProtocol,
                             in viewController: ADManagerLaunchDelegate) {
        DispatchQueue.main.asyncAfter(deadline: .now()+2) {
            self.appDidFinishLaunch = true
        }
        guard canShowAd else { return }
        
        delegate = viewController
        requestAd(for: scene) { [weak self, weak viewController] state in
            guard state == .loadSuccess, let VC = viewController else {
                return
            }
            if let ad = self?.validAdCache(for: scene)?.ad as? AppOpenAd {
                ad.fullScreenContentDelegate = self
                ad.present(from: VC)
            }
        }
    }
    
    // 显示开屏广告：热启动
    public func showLaunchAdWhenActive(scene: ADSceneProtocol,
                                from viewController: UIViewController) {
        print("can === \(canShowAd),appdid ===\(appDidFinishLaunch)")
        guard canShowAd, appDidFinishLaunch else { return }
        
        if !isBlockupByEvent, !isShowingFullScreenAd {
            if let launch = adCaches.first(where: { $0.scene.adType == .launch }),
                !launch.isExpired,
               let ad = launch.ad as? AppOpenAd {
                ad.fullScreenContentDelegate = self
                ad.present(from: viewController)
            } else {
                // 清除过期开屏广告，重新获取
                clearAd(for: scene)
                requestAd(for: scene)
            }
        }
        isBlockupByEvent = false
    }
}

// MARK: 广告展示逻辑
extension ADManager {
    /// 显示指定场景的广告
    public func showAd(scene: ADSceneProtocol,
                        from viewController: UIViewController,
                        completionHandler: ADCompletionHandler? = nil) {
        guard canShowAd else {
            completionHandler?(.fail)
            return
        }
        
        guard let adModel = validAdCache(for: scene) else {
            // 无缓存 || 已过期，本次不显示，提前加载下一次广告
            clearAd(for: scene)
            requestAd(for: scene)
            completionHandler?(.fail)
            return
        }
        switch scene.adType {
        case .launch:
            if let launchAd = adModel.ad as? AppOpenAd {
                adModel.actionHandler = completionHandler
                launchAd.fullScreenContentDelegate = self
                launchAd.present(from: viewController)
            }
            
        case .interstitial:
            if let interstitialAd = adModel.ad as? InterstitialAd {
                adModel.actionHandler = completionHandler
                interstitialAd.fullScreenContentDelegate = self
                interstitialAd.present(from: viewController)
            }
            
        case .reward:
            if let rewardAd = adModel.ad as? RewardedAd {
                adModel.actionHandler = completionHandler
                rewardAd.fullScreenContentDelegate = self
                rewardAd.present(from: viewController) {}
            }
            
        case .unknow:
            break
        }
    }
}

// MARK: 广告数据请求
extension ADManager {
    // 获取场景对应的广告
    public func requestAd(for scene: ADSceneProtocol,
                   completionHandler: ADCompletionHandler? = nil) {
        guard canShowAd else {
            completionHandler?(.fail)
            return
        }
        
        if let _ = self.validAdCache(for: scene) {
            completionHandler?(.loadSuccess)
            return
        }
        
        let adId = scene.adId
        
        switch scene.adType {
        case .launch:
            // 开屏广告
            AppOpenAd.load(with: adId,
                           request: Request()) { [weak self] (ad, error) in
                guard let self = self, let ad = ad else {
                    completionHandler?(.fail)
                    return
                }
                self.saveAdCache(with: ad, for: scene)
                completionHandler?(.loadSuccess)
            }
            
        case .interstitial:
            // 插屏广告
            InterstitialAd.load(with: adId,
                                request: Request()) { [weak self] (ad, error) in
                guard let ad = ad else {
                    completionHandler?(.fail)
                    return
                }
                self?.saveAdCache(with: ad, for: scene)
                completionHandler?(.loadSuccess)
            }
            
        case .reward:
            // 激励广告
            RewardedAd.load(with: adId,
                            request: Request()) { [weak self] ad, error in
                guard let ad = ad else {
                    completionHandler?(.fail)
                    return
                }
                self?.saveAdCache(with: ad, for: scene)
                completionHandler?(.loadSuccess)
            }
            
        case .unknow:
            break
        }
    }
}
